package com.wzh.glsc.order.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rabbitmq.client.Channel;
import com.wzh.common.utils.PageUtils;
import com.wzh.common.utils.Query;
import com.wzh.common.utils.R;
import com.wzh.common.vo.MemberVo;
import com.wzh.glsc.order.constant.OrderConstant;
import com.wzh.glsc.order.dao.OrderItemDao;
import com.wzh.glsc.order.entity.OrderEntity;
import com.wzh.glsc.order.entity.OrderItemEntity;
import com.wzh.glsc.order.entity.OrderReturnReasonEntity;
import com.wzh.glsc.order.feign.CartServiceFeign;
import com.wzh.glsc.order.feign.MemberServiceFeign;
import com.wzh.glsc.order.feign.ProductServiceFeign;
import com.wzh.glsc.order.feign.WareServiceFeign;
import com.wzh.glsc.order.interceptor.OrderInterceptor;
import com.wzh.glsc.order.service.OrderItemService;
import com.wzh.glsc.order.service.to.SkuHasStockTo;
import com.wzh.glsc.order.vo.*;
import lombok.extern.log4j.Log4j2;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;


@Log4j2
@RabbitListener(queues = {"hello-java-queue"})
@Service("orderItemService")
public class OrderItemServiceImpl extends ServiceImpl<OrderItemDao, OrderItemEntity> implements OrderItemService {

    private static ThreadLocal<OrderSubmitVo> orderThreadLocal=new ThreadLocal<>();

    @Autowired
    private MemberServiceFeign memberServiceFeign;
    @Autowired
    private CartServiceFeign cartServiceFeign;
    @Autowired
    private ProductServiceFeign productServiceFeign;
    @Autowired
    private WareServiceFeign wareServiceFeign;
    @Autowired
    private ThreadPoolExecutor executor;
    @Autowired
    StringRedisTemplate redisTemplate;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderItemEntity> page = this.page(
                new Query<OrderItemEntity>().getPage(params),
                new QueryWrapper<OrderItemEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public OrderConfirmVo queryOrderCofirm() throws ExecutionException, InterruptedException {
        MemberVo memberVo = OrderInterceptor.toThreadLocal.get();
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
        //获取主线程中的ThreadLocal的数据
        RequestAttributes requestAttributes =   RequestContextHolder.getRequestAttributes();

        //地址
        CompletableFuture<Void> addressCompletableFuture = CompletableFuture.runAsync(() -> {
            //在异步线程的ThreadLoocal中，放入主线程的ThreadLocal
            RequestContextHolder.setRequestAttributes(requestAttributes);
            R r = memberServiceFeign.addressByUserId(memberVo.getId());
            List<MemberAddressVo> addressVos = r.getData(new TypeReference<List<MemberAddressVo>>() {
            });
            orderConfirmVo.setMemberAddressVos(addressVos);
        }, executor);

        //购物项
        CompletableFuture<Void> itemCompletableFuture = CompletableFuture.runAsync(() -> {
            //在异步线程的ThreadLoocal中，放入主线程的ThreadLocal
            RequestContextHolder.setRequestAttributes(requestAttributes);
            R r = cartServiceFeign.getCheckedItem();
            List<OrderItemVo> orderItems = r.getData(new TypeReference<List<OrderItemVo>>() {
            });
            List<OrderItemVo> collect = orderItems.stream().map(obj -> {
                BigDecimal price = productServiceFeign.priceBySkuId(obj.getSkuId());
                obj.setPrice(price);
                return obj;
            }).collect(Collectors.toList());
            orderConfirmVo.setItems(collect);
        }, executor).thenRunAsync(()->{
            List<Long> collect = orderConfirmVo.getItems().stream().map(obj -> obj.getSkuId()).collect(Collectors.toList());
            R r = wareServiceFeign.hasstock(collect);
            List<SkuHasStockTo> skuHasStockToList = r.getData(new TypeReference<List<SkuHasStockTo>>() {
            });
            Map<Long,Boolean> tempSkuHasStockMap=new HashMap<>();
            for (SkuHasStockTo skuHasStockTo : skuHasStockToList){
                tempSkuHasStockMap.put(skuHasStockTo.getSkuId(),skuHasStockTo.getHasStock());
            }
            orderConfirmVo.setStocks(tempSkuHasStockMap);
        },executor);

        //优惠信息，积分
        orderConfirmVo.setIntegration(memberVo.getIntegration());

        //防重复提交令牌
        String token = UUID.randomUUID().toString().replace("-", "");
        redisTemplate.opsForValue().set(OrderConstant.ORDER_TOKEN_PREFIX+memberVo.getId(), token);
        orderConfirmVo.setOrderToken(token);

        CompletableFuture.allOf(addressCompletableFuture,itemCompletableFuture).get();

        return orderConfirmVo;
    }

    @Override
    public SubmitOrderResponseVo submitOrder(OrderSubmitVo orderSubmitVo) {
        orderThreadLocal.set(orderSubmitVo);
        SubmitOrderResponseVo responseVo=new SubmitOrderResponseVo();
        MemberVo memberVo = OrderInterceptor.toThreadLocal.get();

        //验证令牌
        String orderToken = orderSubmitVo.getOrderToken();
        String script = "if redis.call('get',KEYS[1])==ARGV[1] " +
                "               then return redis.call('del',KEY[1])" +
                "   else    return 0 end";

        Long verifyTokenResult = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class),
                                        Arrays.asList(OrderConstant.ORDER_TOKEN_PREFIX + memberVo.getId()),
                                        orderToken);
        //验证令牌失败
        if(verifyTokenResult==0L){
            responseVo.setCode(1);
            return responseVo;
        }

        //创建订单
        OrderEntity orderEntity= createOrder();


        //      锁库存

        //创建失败则返回订单确认页

        //创建成功去支付页面


        return null;
    }

    /**
     * 创建订单
    */
    private OrderEntity createOrder() {
        OrderEntity orderEntity=new OrderEntity();
        //生成订单号
        String orderCode = IdWorker.getTimeId();
        orderEntity.setOrderSn(orderCode);
        MemberVo memberVo = OrderInterceptor.toThreadLocal.get();
        OrderSubmitVo orderSubmitVo = orderThreadLocal.get();
        //用户信息
        orderEntity.setMemberUsername(memberVo.getUsername());
        orderEntity.setNote(orderSubmitVo.getRemarks());
        //收货信息
        R r = wareServiceFeign.fare(orderSubmitVo.getAddrId());
        FareVo fareVo = r.getData(new TypeReference<FareVo>() {
        });
        MemberAddressVo address = fareVo.getAddress();
        if(address.getId()==orderSubmitVo.getAddrId()){
            orderEntity.setReceiverName(address.getName());
            orderEntity.setReceiverPhone(address.getPhone());
            orderEntity.setReceiverPostCode(address.getPostCode());
            orderEntity.setReceiverProvince(address.getProvince());
            orderEntity.setReceiverCity(address.getCity());
            orderEntity.setReceiverRegion(address.getRegion());
            orderEntity.setReceiverDetailAddress(address.getDetailAddress());
        }
        //获取运费
        orderEntity.setFreightAmount(fareVo.getFare());

        //创建所有订单项
        List<OrderItemEntity> orderItemEntities = buildOrderItems();

        //验价


        return  null;
    }

    /**创建所有订单项*/
    private List<OrderItemEntity> buildOrderItems()
    {
        R r = cartServiceFeign.getCheckedItem();
        List<OrderItemVo> orderItem = r.getData(new TypeReference<List<OrderItemVo>>() {
        });
        List<List<OrderItemVo>> collect = orderItem.stream().map(obj -> {
            OrderItemEntity orderItemEntity = buildOrderItem(obj);

            return orderItem;
        }).collect(Collectors.toList());
        //      需要获取到所有订单项,计算所有项价格,每项锁库存等

        return null;
    }

    /**
     * 创建订单项
     * @param obj
     * @return
     */
    private OrderItemEntity buildOrderItem(OrderItemVo obj) {

        return null;
    }

    //使用RabbitListener注解来监听并接收消息,该注解必须是放在容器里的类中
    // @RabbitListener 参数：
    //          queues ：是一个数组，表示监听哪些队列(queue)，必须先开启@EnableRabbit注解，返回一个Message类型的数据
    //                  多个客户端，同一个消息只能一个客户端能收到消息。
    //                  处理完一条后，方法执行结束。就可以接收到下一条消息
    //方法接收参数：
    //      Message : 包含请求头和请求体，原生的消息类型。通过getBody获取消息体，getMessageProperties获取消息头
    //                 取出请求体后，需要将内容转成对象
    //      T<发送消息的类型> : 发送的消息对象是什么类型这里就写什么：OrderReturnReasonEntity reasonEntity
    //      Channel : 信道，在连接中建立的信道。一个客户端只有一个信道;com.rabbitmq.client.Channel;包下
//    @RabbitListener(queues ={"hello-java-queue"})
    @RabbitHandler
    public void recieveMessage(Message message,
                               OrderReturnReasonEntity reasonEntity,
                               Channel channel) {
//        byte[] body = message.getBody();
//        MessageProperties messageProperties = message.getMessageProperties();
//        log.info("原生消息【{}】,消息体【{}】,消息头【{}】",message,body,messageProperties);
        //消息头属性信息
        log.info("接收OrderReturnReasonEntity对象:{}", reasonEntity.getName());
        MessageProperties messageProperties = message.getMessageProperties();
        //获取到信道中的消息id。该消息id是按接收消息的顺序自增排序
        long deliveryTag = messageProperties.getDeliveryTag();
        try {
            //使用信道中的ack来签收
//            if(deliveryTag%4==0){
            // basicAck(参数1：信道中自增的消息id，参数2：是否是批量签收 ，false是一次只签收本消息)
            // TODO 这里处理不了业务，会报错 UncategorizedAmqpException
            channel.basicAck(deliveryTag, false);
            System.out.println("签收：" + deliveryTag + " ，成功");
//            }else{
//                //使用basicNack 来拒签
//                // basicNack(参数1：信道中自增的消息id，参数2：是否是批量签收 false是一次只签收本消息，参数3：拒签后消息是否放回队列中 true为放回)
//                channel.basicNack(deliveryTag,false,true);
//                System.out.println("已拒签："+deliveryTag);
//            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @RabbitHandler
    public void recieveMessage(OrderEntity reasonEntity) {
//        byte[] body = message.getBody();
//        MessageProperties messageProperties = message.getMessageProperties();
//        log.info("原生消息【{}】,消息体【{}】,消息头【{}】",message,body,messageProperties);
        log.info("接收OrderEntity对象:{}", reasonEntity);
    }

}